package com.test.platform.module.perf.service;

import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.test.common.component.RedisComponent;
import com.test.common.entity.perf.PerfResourceEntity;
import com.test.common.entity.perf.PerfScriptEntity;
import com.test.common.enums.perf.PerfScriptPlatformEnum;
import com.test.common.form.perf.ScriptModifyForm;
import com.test.common.repository.perf.PerfResourceRepository;
import com.test.common.repository.perf.PerfScriptRepository;
import com.test.common.response.BizException;
import com.test.common.utils.SystemUtils;
import com.test.platform.module.perf.Config;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsCriteria;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
public class PerfScriptService {

    @Resource(name = "perf_config")
    private Config config;

    @Resource
    private GridFsTemplate gridFsTemplate;

    @Resource
    private GridFSBucket gridFSBucket;

    @PostConstruct
    public void init() {
        log.info("INIT PerfScriptService");
    }

    @Resource
    private RedisComponent redisComponent;

    @Resource
    private PerfScriptRepository perfScriptRepository;

    @Resource
    private PerfResourceRepository perfResourceRepository;

    @SneakyThrows
    public HashMap<String, ArrayList<String>> fileUpload(List<FileItem> fileItems) {

        HashMap<String, ArrayList<String>> data = new HashMap<>();
        data.put("OK", new ArrayList<>());
        data.put("KO", new ArrayList<>());

        for (FileItem fileItem : fileItems) {

            if (fileItem.isFormField()) {
                log.warn("这里不需要传非文件内容|{}|{}", fileItem.getFieldName(), fileItem.getString("utf-8"));
            } else {
                String fileName = fileItem.getName().substring(fileItem.getName().lastIndexOf("\\") + 1);

                PerfScriptPlatformEnum platform;
                if (StringUtils.endsWith(fileName, ".jmx")) {
                    platform = PerfScriptPlatformEnum.JMETER;
                } else if (StringUtils.endsWith(fileName, ".scala")) {
                    platform = PerfScriptPlatformEnum.GATLING;
                } else {
                    data.get("KO").add(fileName);
                    log.info("仅支持上传Gatling或JMeter脚本|{}", fileItem);
                    continue;
                }

                log.info("上传文件|{}", fileName);

                // 先保存文件
                String objectId = gridFsTemplate.store(fileItem.getInputStream(), fileName).toHexString();

                // 再更新数据库
                Optional<PerfScriptEntity> optional = perfScriptRepository.findByName(fileName);
                if (optional.isPresent()) {

                    log.info("数据已存在，修改老数据");
                    PerfScriptEntity perfResourceEntity = optional.get();

                    // 数据入库
                    perfResourceEntity = new PerfScriptEntity(perfResourceEntity.getId(), fileName, SystemUtils.getUser().getName(), objectId,
                            platform, fileItem.getSize() / 1024);

                    perfScriptRepository.save(perfResourceEntity);
                } else {

                    log.info("数据不存在，添加新数据");

                    // 数据入库
                    PerfScriptEntity perfResourceEntity = new PerfScriptEntity(fileName, SystemUtils.getUser().getName(), objectId, platform, fileItem.getSize() / 1024);
                    perfScriptRepository.save(perfResourceEntity);
                }

                data.get("OK").add(fileName);
            }
        }

        return data;
    }

    public HashMap<String, String> detail(int id) {

        Optional<PerfScriptEntity> optional = perfScriptRepository.findById(id);

        try {
            if (optional.isPresent()) {
                PerfScriptEntity perfScriptEntity = optional.get();
                GridFSFile fsFile = gridFsTemplate.findOne(Query.query(GridFsCriteria.where("_id").is(perfScriptEntity.getObjectId())));
                if (fsFile != null) {
                    if (fsFile.getLength() > 1000000) {
                        throw new BizException("文件过大，无法预览：" + (fsFile.getLength() / 1024.0));
                    }
                    GridFsResource fsResource = new GridFsResource(fsFile, gridFSBucket.openDownloadStream(fsFile.getObjectId()));
                    HashMap<String, String> result = new HashMap<>();
                    result.put("content", IOUtils.toString(fsResource.getInputStream(), StandardCharsets.UTF_8));
                    if (perfScriptEntity.getName().endsWith(".jmx")) {
                        result.put("format", "xml");
                    } else if (perfScriptEntity.getName().endsWith(".scala")) {
                        result.put("format", "scala");
                    } else {
                        result.put("format", "text");
                    }
                    return result;
                } else {
                    throw new BizException("脚本文件丢失：" + perfScriptEntity.getObjectId());
                }
            } else {
                throw new BizException("未找到脚本文件：" + id);
            }
        } catch (Exception e) {
            log.error("获取文件信息异常|{}", e.getMessage());
            e.printStackTrace();
            throw new BizException("获取文件信息异常", e);
        }
    }

    public void download(int id, HttpServletResponse response) {

        Optional<PerfScriptEntity> optional = perfScriptRepository.findById(id);

        try {
            if (optional.isPresent()) {
                PerfScriptEntity perfScriptEntity = optional.get();
                GridFSFile fsFile = gridFsTemplate.findOne(Query.query(GridFsCriteria.where("_id").is(perfScriptEntity.getObjectId())));
                if (fsFile != null) {
                    if (fsFile.getLength() > 1000000) {
                        throw new BizException("文件过大，无法预览：" + (fsFile.getLength() / 1024.0));
                    }
                    GridFsResource fsResource = new GridFsResource(fsFile, gridFSBucket.openDownloadStream(fsFile.getObjectId()));
                    log.info("文件大小|{}", fsResource.contentLength());

                    InputStream inputStream = fsResource.getInputStream();
                    ServletOutputStream outputStream = response.getOutputStream();
                    response.setContentType("application/octet-stream");
                    response.setHeader("Content-disposition", "attachment;filename=" + fsResource.getFilename());
                    response.setContentLengthLong(fsResource.contentLength());
                    int len;
                    byte[] buffer = new byte[1024 * 1024 * 100];
                    while ((len = inputStream.read(buffer)) != -1) {
                        outputStream.write(buffer, 0, len);
                        outputStream.flush();
                    }

                    response.flushBuffer();
                    outputStream.close();
                    inputStream.close();
                } else {
                    throw new BizException("脚本文件丢失：" + perfScriptEntity.getObjectId());
                }
            } else {
                throw new BizException("未找到脚本文件：" + id);
            }
        } catch (Exception e) {
            log.error("获取文件信息异常|{}", e.getMessage());
            e.printStackTrace();
            throw new BizException("获取文件信息异常", e);
        }
    }

    @Transactional
    public String delete(int id) {

        Optional<PerfScriptEntity> optional = perfScriptRepository.findById(id);

        if (optional.isPresent()) {
            PerfScriptEntity perfScriptEntity = optional.get();

            // 删除存储文件
            gridFsTemplate.delete(Query.query(Criteria.where("_id").is(perfScriptEntity.getObjectId())));

            // 删除资源文件关联关系
            perfScriptEntity.setResources(new ArrayList<>());

            // 删除脚本文件
            perfScriptRepository.delete(perfScriptEntity);
            return "删除成功";
        } else {
            throw new BizException("未找到资源文件：" + id);
        }
    }

    // 页码前端从1开始计算，后端从0开始计算
    public Page<PerfScriptEntity> findAll(int page, int size) {
        page = page - 1;
        return perfScriptRepository.findAll(PageRequest.of(page, size, Sort.by("id").descending()));
    }

    @Transactional(rollbackOn = Exception.class)
    public Object updateResource(int scriptId, int[] resourceIds) {

        Optional<PerfScriptEntity> optional = perfScriptRepository.findById(scriptId);

        if (optional.isPresent()) {
            PerfScriptEntity perfScriptEntity = optional.get();
            perfScriptEntity.setResources(new ArrayList<>());
            for (int resourceId : resourceIds) {
                Optional<PerfResourceEntity> resourceEntityOptional = perfResourceRepository.findById(resourceId);
                if (resourceEntityOptional.isPresent()) {
                    perfScriptEntity.getResources().add(resourceEntityOptional.get());
                } else {
                    throw new BizException("未找到资源文件：" + resourceId);
                }
            }

            return "脚本资源文件更新成功";
        } else {
            throw new BizException("未找到脚本文件：" + scriptId);
        }
    }

    @Transactional
    public Object updateContent(ScriptModifyForm scriptModifyForm) {

        Optional<PerfScriptEntity> optional = perfScriptRepository.findById(scriptModifyForm.getId());

        if (optional.isPresent()) {
            PerfScriptEntity perfScriptEntity = optional.get();
            // 先删除旧文件
            gridFsTemplate.delete(Query.query(GridFsCriteria.where("_id").is(perfScriptEntity.getObjectId())));
            // 再保存新文件
            String objectId = gridFsTemplate.store(new ByteArrayInputStream(scriptModifyForm.getContent().getBytes()), perfScriptEntity.getName()).toHexString();
            // 更新objectId
            perfScriptEntity.setObjectId(objectId);
            return "脚本资源文件更新成功";
        } else {
            throw new BizException("未找到脚本文件：" + scriptModifyForm.getId());
        }
    }
}
